package cbfg.http

import cbfg.http.constant.ThreadMode
import cbfg.http.subscribe.Disposable
import cbfg.http.subscribe.Observable
import cbfg.http.subscribe.Observer
import cbfg.http.util.Logger
import cbfg.http.util.ThreadUtils
import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import java.lang.reflect.Type
import java.net.UnknownServiceException

/**
 * 网络请求，每一次请求都会创建一个请求类OkHttpCall
 */
class OkHttpCall<T> internal constructor(
    private val hostTag: String?,
    private val httpTag: String?,
    private val returnType: Type
) : Observable<T>, Disposable {
    private val httpExecuteThread = ThreadMode.ASYNC
    private var subscribeThread = ThreadMode.ASYNC
    private var observeThread = ThreadMode.MAIN

    private lateinit var request: Request
    private lateinit var call: Call
    private lateinit var observer: Observer<T>

    private var disposed = false

    fun prepare(request: Request): Observable<T> {
        this.request = request
        return this
    }

    override fun subscribeOn(threadMode: ThreadMode): Observable<T> {
        this.subscribeThread = threadMode
        return this
    }

    override fun observeOn(threadMode: ThreadMode): Observable<T> {
        this.observeThread = threadMode
        return this
    }

    override fun subscribe(observer: Observer<T>) {
        this.observer = observer
        ThreadUtils.run(observeThread) { this.observer.onSubscribe(this) }
        checkExecute {
            call = HttpManager.getOkHttpClient().newCall(request)
            ThreadUtils.run(httpExecuteThread) { execute() }
        }
    }

    /**
     * 执行请求
     */
    private fun execute() {
        try {
            val response = call.execute()
            checkExecute {
                when {
                    response.code() != 200 -> {
                        val string = response.body()?.string() ?: response.message()
                        throw UnknownServiceException(string)
                    }
                    subscribeThread == httpExecuteThread -> onResult(response)
                    else -> ThreadUtils.run(subscribeThread) { onResult(response) }
                }
            }
        } catch (e: Exception) {
            checkExecute { onError(e) }
        }
    }

    private inline fun checkExecute(action: () -> Unit) {
        if (!disposed) {
            action.invoke()
        }
    }

    /**
     * 处理响应结果
     */
    @Suppress("UNCHECKED_CAST")
    @Throws(Exception::class)
    private fun onResult(response: Response) {
        var string = response.body()?.string() ?: response.message()
        Logger.i("[Result of ${request.url()}] : $string \n")
        string = HttpManager.converterFactory.convertResponse(hostTag, httpTag, string)
        if (returnType == String::class.java) {
            onSuccess(string as T)
        } else {
            onSuccess(HttpManager.gson.fromJson(string, returnType))
        }
    }

    /**
     * 回调成功结果
     */
    private fun onSuccess(t: T) {
        ThreadUtils.run(observeThread) {
            observer.onNext(t)
            observer.onComplete()
            disposed = true
        }
    }

    /**
     * 回调错误信息
     */
    private fun onError(e: Throwable) {
        ThreadUtils.run(observeThread) {
            observer.onError(e)
            observer.onComplete()
            disposed = true
        }
    }

    override fun isDisposed(): Boolean = disposed

    override fun dispose() {
        disposed = true
        call.cancel()
    }
}